package com.corpgovernment.common.utils;

import com.corpgovernment.common.enums.DateFormatEnum;
import com.google.common.collect.Lists;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @Author: majl
 * @DateTime: 2019-5-20
 * @FileName: DateUtil
 * @Description
 */
public class DateUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(DateUtil.class);

    public static String DF_YMD_HM_SLASH = "yyyy/MM/dd HH:mm";
    public static String DF_YMD_SLASH = "yyyy/MM/dd";
    public static String DF_YM = "yyyy-MM";
    public static String DF_Y = "yyyy";
    public static String DF_YM_CN = "yyyy年MM月";
    public static String DF_YMD_HMS = "yyyy-MM-dd HH:mm:ss";
    public static String DF_YMD_HM = "yyyy-MM-dd HH:mm";
    public static String DF_YMD = "yyyy-MM-dd";
    public static String DF_MD = "MM-dd";
    public static String DFYMD = "yyyyMMdd";
    public static String DF_YMD_DOT = "yyyy.MM.dd";
    public static String DF_YMD_CN = "yyyy年MM月dd日";
    public static String DF_YMDHM_CN = "yyyy年MM月dd日 HH:mm";
    public static String DF_MD_CN = "MM月dd日";
    public static String DF_HM = "HH:mm";
    public static String[] WEEK_DAY = {"日", "一", "二", "三", "四", "五", "六"};
    public static String DF_YMD_ZERO = "yyyy-MM-dd 00:00:00";
    /**
     * MM月dd日 HH:mm
     */
    public static String DF_MD_HM = "MM月dd日 HH:mm";
	public static String DF_YMDHMS = "yyyyMMddHHmmss";


	public static String dateToString(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD_HMS);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String getYesterdayYMD() {
        try {
            Calendar c = Calendar.getInstance();
            c.add(Calendar.DAY_OF_MONTH, -1);
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD);
            return sdf.format(c.getTime());
        } catch (Exception e) {
        }
        return null;
    }

    public static String getNowDayYMD() {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD);
            return sdf.format(new Date());
        } catch (Exception e) {
        }
        return null;
    }

    public static String dateToYMD_HM(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD_HM);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String dateToYMD(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String dateToMD(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_MD);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String dateToHM(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_HM);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String dateToYMD_DOT(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD_DOT);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String dateToYMD_CN(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD_CN);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String dateToYMDHM_CN(Date date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMDHM_CN);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static String getNowDay() {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DFYMD);
            return sdf.format(new Date());
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 判断日期格式是否为yyyy-MM-dd HH:mm:ss
     *
     * @param date
     * @return
     */
    public static boolean isFullDate(String date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD_HMS);
            sdf.parse(date);
            return true;
        } catch (Exception e) {
        }
        return false;
    }

    /**
     * 判断日期格式是否为yyyy-MM-dd
     *
     * @param date
     * @return
     */
    public static boolean isDateDay(String date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD);
            sdf.parse(date);
            return true;
        } catch (Exception e) {
        }
        return false;
    }

    public static String dateToString(Date date, String pattern) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            return sdf.format(date);
        } catch (Exception e) {
        }
        return null;
    }

    public static Date stringToDate(String date, String pattern) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            return sdf.parse(date);
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 日期相加
     */
    public static String addDays(String timeStr, String format, Integer addDays) {
        try {
            Date date = DateUtils.parseDate(timeStr, format);
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(date.getTime());
            calendar.add(Calendar.DAY_OF_MONTH, addDays);
            date.setTime(calendar.getTimeInMillis());
            return DateFormatUtils.format(date, DateFormatEnum.SIMPLEDD.getFormat());
        } catch (Exception e) {
            LOGGER.warn("日期转换异常", e);
            throw new RuntimeException("日期转换异常");
        }
    }

    /**
     * 日期相加
     */
    public static Date addDays(Date date, Integer addDays) {
        try {
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(date.getTime());
            calendar.add(Calendar.DAY_OF_MONTH, addDays);
            return new Date(calendar.getTimeInMillis());
        } catch (Exception e) {
            LOGGER.warn("日期转换异常", e);
            throw new RuntimeException("日期转换异常");
        }
    }

	/**
	 * 日期相加
	 */
	public static Date addMinutes(Date date, Integer addMinutes) {
		try {
			Calendar calendar = Calendar.getInstance();
			calendar.setTimeInMillis(date.getTime());
			calendar.add(Calendar.MINUTE, addMinutes);
			return new Date(calendar.getTimeInMillis());
		} catch (Exception e) {
			LOGGER.warn("日期转换异常", e);
			throw new RuntimeException("日期转换异常");
		}
	}

    /**
     * 月相加
     */
    public static String addMonths(String timeStr, String format, Integer addMonths) {
        try {
            Date date = DateUtils.parseDate(timeStr, format);
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(date.getTime());
            calendar.add(Calendar.MONTH, addMonths);
            date.setTime(calendar.getTimeInMillis());
            return DateFormatUtils.format(date, DateFormatEnum.SIMPLEDD.getFormat());
        } catch (Exception e) {
            LOGGER.warn("日期转换异常", e);
            throw new RuntimeException("日期转换异常");
        }
    }
    /**
     * 当前时间在指定时间多少毫秒前
     */
    public static Boolean greaterThanNow(String timeStr, Long timeMillis) {
        try {
            Date date = DateUtils.parseDate(timeStr, DateFormatEnum.SIMPLE.getFormat());
            return greaterThanNow(date, timeMillis);
        } catch (Exception e) {
            LOGGER.warn("日期转换异常", e);
            throw new RuntimeException("日期转换异常");
        }
    }

    /**
     * 当前时间在指定时间多少毫秒前
     */
    public static Boolean greaterThanNow(Date date, Long timeMillis) {
        Long subtract = date.getTime() - System.currentTimeMillis();
        return subtract >= timeMillis;
    }

    /**
     * 计算间隔天数
     */
    public static Integer betweenDay(Date beginTime, Date endTime) {
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime bt = LocalDateTime.ofInstant(beginTime.toInstant(), zone);
        LocalDateTime et = LocalDateTime.ofInstant(endTime.toInstant(), zone);
        long betweenDay = et.toLocalDate().toEpochDay() - bt.toLocalDate().toEpochDay();
        return (int) Math.abs(betweenDay);
    }

    /**
     * 计算间隔天数
     */
    public static Long subtractDay(Date beginTime, Date endTime) {
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime bt = LocalDateTime.ofInstant(beginTime.toInstant(), zone);
        LocalDateTime et = LocalDateTime.ofInstant(endTime.toInstant(), zone);
        long betweenDay = et.toLocalDate().toEpochDay() - bt.toLocalDate().toEpochDay();
        return betweenDay;
    }

    /**
     * 计算间隔分钟
     *
     * @param dt
     * @param at
     * @return
     */
    public static Long betweenMinutes(Date dt, Date at) {
        Instant fromDate = dt.toInstant();
        Instant toDate = at.toInstant();
        return ChronoUnit.MINUTES.between(fromDate, toDate);
    }

    /**
     * 获取3月31日格式日期
     */
    public static String getMonthDay(Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        Integer month = calendar.get(Calendar.MONTH) + 1;
        Integer day = calendar.get(Calendar.DAY_OF_MONTH);
        return String.format("%s月%s日", month, day);
    }

    /**
     * 获取星期
     */
    public static String getWeekDay(String prefix, Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        String weekDay = WEEK_DAY[calendar.get(Calendar.DAY_OF_WEEK) - 1];
        return prefix + weekDay;
    }

    /**
     * 获取当日零点时间
     * @return
     */
    public static String stringToDayZero(String dateStr,String format) throws ParseException {
        return dateToString(DateUtils.parseDate(dateStr, format),DF_YMD_ZERO);
    }

    /**
     * 获取前month月的时间
     * 如9月20日 返回6月20日
     * @param month
     * @return
     */
    public static String getMonthDayBeforeNow(int month){
        Date dNow = new Date();
        Date dBefore = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dNow);
        calendar.add(Calendar.MONTH, -month);
        dBefore = calendar.getTime();   //得到前3月的时间
        SimpleDateFormat sdf=new SimpleDateFormat(DFYMD);
        String defaultStartDate = sdf.format(dBefore);    //格式化前month月的时间
        return defaultStartDate;
    }

    /**
     * MM月dd日 HH:mm 格式器
     */
    private static final DateTimeFormatter MM_Y_DD_R_HH_MM_FORMATTER = DateTimeFormatter.ofPattern("MM月dd日 HH:mm");
    /**
     * yyyy-MM-dd HH:mm:ss 格式器
     */
    private static final DateTimeFormatter YYYY_MM_DD_HH_MM_SS_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd " +
                                                                                                            "HH:mm:ss");

    /**
     * 返回格式为{@code MM月dd日 HH:mm}的时间字符串
     *
     * @param date 日期
     * @return 日期为空返回{@code}
     */
    public static String getMmDdHhMmString(@NonNull Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(MM_Y_DD_R_HH_MM_FORMATTER);
    }
    /**
     * 返回格式为{@code MM月dd日 HH:mm}的时间字符串
     *
     * @param dateString 日期字符串
     * @return 日期为空返回{@code}
     */
    public static String getMmDdHhMmString(@NonNull String dateString) {
       return   LocalDateTime.parse(dateString, YYYY_MM_DD_HH_MM_SS_FORMATTER).format(MM_Y_DD_R_HH_MM_FORMATTER);
    }

    /**
     * 获取两个时间间隔之间的所有年份（倒序） yyyy
     */
    public static List<String> getYearInterval(Date begin, Date end) {
        List<String> years = Lists.newArrayList();
        String bstr = dateToString(begin, DF_Y);
        int year1 = StringUtils.isBlank(bstr) ? 0 : Integer.parseInt(bstr);
        String estr = dateToString(end, DF_Y);
        int year2 = StringUtils.isBlank(estr) ? 0 : Integer.parseInt(estr);
        for (int i = year2; i >= year1 && i > 0; i--) {
            years.add(String.valueOf(i));
        }
        return years;
    }

    /**
     * 获取两个时间间隔之间的所有日期（升序） yyyyMMdd
     */
    public static List<String> getDayInterval(Date begin, Date end) {
        List<String> days = Lists.newArrayList();
        String bstr = dateToString(begin, DFYMD);
        int day1 = StringUtils.isBlank(bstr) ? 0 : Integer.parseInt(bstr);
        String estr = dateToString(end, DFYMD);
        int day2 = StringUtils.isBlank(estr) ? 0 : Integer.parseInt(estr);
        for (int i = day2; i >= day1 && i > 0; i--) {
            days.add(String.valueOf(i));
        }
        return days;
    }

    /**
     * 比较两个日期的时分
     * @return
     * 0:时分相等
     * 1:xDate时分在yDate时分之前
     * -1:xDate时分在yDate时分之后
     */
    public static int compareHourAndMinute(Date xDate,Date yDate) {
        xDate = DateUtil.stringToDate(
                DateUtil.dateToString(xDate,DateUtil.DF_HM),
                DateUtil.DF_HM
        );
        yDate = DateUtil.stringToDate(
                DateUtil.dateToString(yDate,DateUtil.DF_HM),
                DateUtil.DF_HM
        );
        if (xDate == null || yDate == null){
            throw new RuntimeException("非法参数");
        }
        if (xDate.after(yDate)){
            return -1;
        }
        else if (xDate.equals(yDate)){
            return 0;
        }
        else {
            return 1;
        }
    }

	public static String getAddDaysYMD(int addDays) {
		try {
			Calendar c = Calendar.getInstance();
			c.add(Calendar.DAY_OF_MONTH, addDays);
			SimpleDateFormat sdf = new SimpleDateFormat(DF_YMD);
			return sdf.format(c.getTime());
		} catch (Exception e) {
		}
		return null;
	}

    //获取日（月中的某一天）
    public static Integer getNowDateDay() {
        Calendar cal = Calendar.getInstance();
        Date date=new Date();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_MONTH);
    }

    public static Integer getNowDateMoon() {
        Calendar cal = Calendar.getInstance();
        Date date=new Date();
        cal.setTime(date);
        return cal.get(Calendar.MONTH)+1;
    }

    public static Integer getNowDateYear() {
        Calendar cal = Calendar.getInstance();
        Date date=new Date();
        cal.setTime(date);
        return cal.get(Calendar.YEAR);
    }

    public static Date createDate(Integer year, Integer moon, Integer day) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, moon - 1, day);
        return cal.getTime();
    }

    public static Date createDate(Integer year, Integer moon, Integer day, int hourOfDay, int minute,
                                  int second) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, moon - 1, day, hourOfDay, minute, second);
        return cal.getTime();
    }

    /**
     * 判断当前时间是否在[startTime, endTime]区间，注意三个参数的时间格式要一致
     * @param nowTime
     * @param startTime
     * @param endTime
     * @return 在时间段内返回true，不在返回false
     */
    public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
        if (nowTime.getTime() == startTime.getTime()
                || nowTime.getTime() == endTime.getTime()) {
            return true;
        }

        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);

        Calendar begin = Calendar.getInstance();
        begin.setTime(startTime);

        Calendar end = Calendar.getInstance();
        end.setTime(endTime);

        return date.after(begin) && date.before(end);
    }
}